let a = 1;
let b = [1, null];

let c = (x = 1) => x + 1;
//最佳通用类型推断， 以上均为从右向左推断

//上下文类型推断，(多出现在事件处理中


interface Foo {
    bar: number;
}

//以下为类型断言，可以将一个变量直接指定为某一类型
//let foo = {} as Foo;
let foo: Foo = {
    bar: 1
};

/**
 * 当一个类型Y可以被赋值给另一个类型X时，我们就可以说类型X兼容类型Y
 * X兼容Y=》 X（目标类型) = Y(源类型)
 */
let s: string = 'a';

s = null;
/**
 * 接口兼容
 */
interface X {
    a: any;
    b: any;
}

interface Y {
    a: any;
    b: any;
    c: any;
}
{
    let x: X = {
        a: 1,
        b: 2
    };
    let y: Y = {
        a: 1,
        b: 2,
        c: 3
    };
    x = y;//只要实现y接口的变量具有实现X接口的所有变量，那么y就可以赋值给x
}

/**
 * 判定函数的兼容性一般出来在两个函数相互赋值的时候，函数做为参数的情况下
 */
type Handler = (a: number, b: number) => void;
function hof(handler: Handler) {
    return handler;
}
/**
 * 需要满足三个条件
 * 1. 参数个数
 */
{
    let handler1 = (a: number) => { };
    hof(handler1);

    let handler2 = (a: number, b: number, c: number) => { };

    //存在可选参数，剩余参数的情况下
    let a = (p1: number, p2: number) => { };
    let b = (p1: number, p2?: number) => { };
    let c = (...args: number[]) => { };
    a = b;
    a = c;
}

/**
 * 2. 参数类型要匹配
 */

{
    let handler3 = (a: string) => { };
    //  hof(handler3);
}

interface Point3D {
    x: number;
    y: number;
    z: number;
}
interface Point2D {
    x: number;
    y: number;
}
let p3d = (point: Point3D) => {
    console.log(point.x);
    console.log(point.y);
    console.log(point.z);
};
let p2d = (point: Point2D) => { };
p3d = p2d;

/**
 * 3) 返回值类型
 *  目标函数返回值类型必须与源函数的返回值类型相同或与这兼容
 */

let f = () => ({ name: 'Alice' });

let g = () => ({
    name: 'Alice',
    age: 19
});
f = g;

/**
 * 函数重载
 */

function overload(a: number, b: number): number;
function overload(a: string, b: string): number;
function overload(a: any, b: any): any { };

//枚举兼容性
enum Color {
    Red,
    Yellow
}
enum Fruit {
    Apple,
    Banana
}
//枚举类型与数值类型完全兼容
let fruit: Fruit.Apple = 1;
let no: number = Fruit.Banana;
//枚举间是完全不兼容的

// let color: Color.Red = Fruit.Apple;

/**
 * 娄的兼容性
 * 在比较两个类的兼容性时，静态成员和构造函数不参与比较，只比较结构与接口很相似。
 */
class A {
    id: number = 1;
    private name: string = '';
    constructor(p: number, q: number) {

    }
}

class B {
    static s: number = 1;
    private name: string = '';
    id: number = 2;
    constructor(p: number) {

    }
}
{
    let aa = new A(1, 2);
    let bb = new B(1);
    // aa = bb;//两个变量虽然为不同的类，但是可以互相兼容
    // bb = aa;//当类中含有私有成员时，只有父类能兼容子类，
}
class C extends A {

}
{
    let cc = new C(1, 2);
    let aa = new A(2, 3);
    cc = aa;
    aa = cc;
}
//范型兼容性
interface Empty<T> {
    name: string;
    // text: T;
}
let obj1: Empty<number> = { name: 'a' };
let obj2: Empty<string> = { name: 'v' };
obj1 = obj2;//当范型中未定义接口变量时，这两者可以兼容，当定义之后就不兼容

/**
 * 范型函数
 */
let log11 = <T>(x: T): T => {
    console.log(x);
    return x;
}
let log12 = <U>(y: U): U => {
    console.log(y);
    return y;
}

/**
 * 口诀：
 * 结构之间兼容：成员少的兼容成员多的
 * 函数之间兼容：参数多的兼容参数少的
 */


/**
 * ts类型保护机制
 */

/**
 * typescript中的类型保护指typescript能够在特定的区块中保证变量属于
 * 某种确定的类型。可以在此区块中放心地使用此类型的属性，或者调用此类型的方法。
 */
enum Type {
    Strong,
    Week
}

class Java {
    java: any;
    helloJava() {
        console.log('Hello java');
    }
}

class JavaScript {
    javascript: any;
    helloJavaScript() {
        console.log('Hello javascript');
    }
}

//类型 谓词
function isJava(lang: Java | JavaScript) :lang is Java{
    return (lang as Java).helloJava !== undefined;
}

function getLanguage(type: Type, x?: string | number) {
    let lang = type === Type.Strong ? new Java() : new JavaScript();
    // if((lang as Java).helloJava){
    //     (lang as Java).helloJava();
    // } else {
    //     (lang as JavaScript).helloJavaScript();
    // }

    if(isJava(lang)){
        lang.helloJava();
    } else {
        lang.helloJavaScript();
    }

    // instanceof
    // if(lang instanceof Java){
    //     lang.helloJava();
    // } else {
    //     lang.helloJavaScript();
    // }
    // return lang;

    // in
    // if ('java' in lang) {
    //     lang.helloJava();
    // } else {
    //     lang.helloJavaScript();
    // }

    // typeof
    // if (typeof x === 'string') {
    //     x.length
    // } else {
    //     x.toFixed
    // }
}
getLanguage(Type.Strong);


